package com.luzh.myspring.aop.framework.autoproxy;

import com.luzh.myspring.aop.*;
import com.luzh.myspring.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.luzh.myspring.aop.framework.ProxyFactory;
import com.luzh.myspring.beans.BeanException;
import com.luzh.myspring.beans.PropertyValues;
import com.luzh.myspring.beans.factory.BeanFactory;
import com.luzh.myspring.beans.factory.BeanFactoryAware;
import com.luzh.myspring.beans.factory.ConfigurableListableBeanFactory;
import com.luzh.myspring.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * 自动生成被增强类的代理（实例化后）
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;
    private final Set<Object> earlyProxyReferences = Collections.synchronizedSet(new HashSet<>());

    public DefaultAdvisorAutoProxyCreator(ConfigurableListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }

    /**
     * 是否为特殊类
     * @param beanClass
     * @return
     */
    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    /**
     * 判断是否为被增强类，并生成代理替换之
     * @param bean
     * @param beanName
     * @return
     * @throws BeanException
     */
    public Object wrapIfNecessary(Object bean, String beanName) throws BeanException {
        AdvisedSupport advisedSupport = new AdvisedSupport();
        TargetSource targetSource = new TargetSource(bean);
        //跳过特殊类
        if (isInfrastructureClass(targetSource.getTargetClass())) return bean;
        //获取所有顾问
        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            //判断是否为被增强类
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (!classFilter.matches(targetSource.getTargetClass())) continue;
            //封装通知信息
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());//通知转为拦截器
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(true);//代理目标类
            return new ProxyFactory(advisedSupport).getProxy();
        }
        return bean;
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeanException {
        return null;
    }

    /**
     * 如果遇到循环依赖，匿名工厂会使用该方法判断是否需要提前生成代理，以实现aop
     * @param bean
     * @param beanName
     * @return
     */
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        // 提前生成代理需要记录
        earlyProxyReferences.add(beanName);
        return wrapIfNecessary(bean, beanName);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {
        return bean;
    }

    /**
     * 初始化后生成代理，如果bean被aop增强，需要判断是否在之前已经由匿名工厂创建了代理
     * @param bean
     * @param beanName
     * @return
     * @throws BeanException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException {
        if (!earlyProxyReferences.contains(beanName)) {
            return wrapIfNecessary(bean, beanName);
        }
        return bean;
    }
}
